package thread;

import java.util.LinkedList;

/**
 * Fixed size ThreadPool pool implementation
 * 
 * @author denist
 * 
 */
public class ThreadPool {


	private int threadCount = 0;
	private boolean shutdownRequested = false;
	private final Worker[] pool;
	private final LinkedList queue;

	private ThreadPool(int size) {
		size = Math.max(1, size);
		this.pool = new Worker[size];
		this.queue = new LinkedList();
		init(size);
	}

	private void init(int size) {
		for (int i = 0; i < size; i++) {
			Worker worker = new Worker(this);
			this.pool[i] = worker;
			worker.start();
		}
	}
	
	/**
	 * Returns new <code>ThreadPool</code> of the specified <code>int</code> size
	 * 
	 * @param size
	 *            the size of the <code>ThreadPool</code>. <br>
	 *            Default pool size is one thread
	 */
	public static ThreadPool newFixedSizeThreadPool(int size){
		return new ThreadPool(size);
	}

	/**
	 * Adds provided <code>Runnable</code> task to the <code>ThreadPool</code>
	 * queue
	 * 
	 * @param task
	 *            Task to be added
	 */
	public synchronized void execute(Runnable task) {
		if (shutdownRequested) {
			throw new RuntimeException("ThreadPool is in shutdown state");
		}
		if (task == null) {
			throw new IllegalArgumentException("Task can not be null");
		}
		increaseThreadCount();
		queue.add(task);
		notifyAll();
	}

	/**
	 * Request shutdown of the <code>ThreadPool</code> and <br>
	 * Blocks calling thread until all work assigned to <code>ThreadPool</code>
	 * is finished
	 */
	public synchronized void shutdown() {
		shutdownRequested = true;
		while (!queue.isEmpty() || threadCount > 0) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		notifyAll();
	}

	private synchronized Runnable getTask() {
		while (queue.isEmpty() && !shutdownRequested) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		Runnable task;
		if (queue.isEmpty() && shutdownRequested) {
			task = null;
		} else {
			task = (Runnable) queue.removeFirst();
		}
		return task;
	}

	private synchronized void increaseThreadCount() {
		threadCount++;
	}

	private synchronized void decreaseThreadCount() {
		threadCount--;
	}

	synchronized boolean isWorkersAlive() {
		for (int k = 0; k < pool.length; k++) {
			if (pool[k].isAlive()) {
				return true;
			}
		}
		return false;
	}

	/*
	 * Private inner class for ThreadPool Worker implementation
	 */
	private final static class Worker extends Thread {

		private final ThreadPool owner;

		public Worker(ThreadPool pool) {
			this.owner = pool;
		}

		public void run() {
			Runnable task;
			do {
				task = owner.getTask();
				if (task != null) {
					try {
						// task can be null to signal that ThreadPool shutdown
						// is
						// requested and
						// worker should finish
						task.run();
					} catch (Throwable tw) {
						// Exception should be handled to avoid reduction of the
						// worker threads
						tw.printStackTrace();
					}
					synchronized (owner) {
						owner.decreaseThreadCount();
						owner.notifyAll();
					}
				}
			} while (task != null);
		}
	}
}
